#include "packet_filter.h"
#include <pcap.h>
#include <iostream>
#include <stdlib.h>
#include <pcap.h>

#include <packet_quer.h>
#include <posix_thread.h>
#include <connection.h>


using namespace std;

struct my_data
{
    char  dev_ptr[256];
    pcap_t *handle;
    PacketQuer *quer;
};


int main()
{
    vector <string> devices;
    devices.push_back("eth0");
    devices.push_back("lo");

    PosixThread *th = new PosixThread;
    PacketQuer& quer = PacketQuer::getInstance(th);
    Connections conn;
    conn.addPacketQuer(&quer); 
    
    try {
        PacketFilter *pktf = new PacketFilter(&quer, devices);
    } catch (const char *str) {
        cout << "Caught " << str << endl;
    } catch (...) {
    }
    while (1) {
        Packet *pkt = conn.getNextPacket();
        if (pkt) {
            conn.addConnectionInfo(*pkt);            
            pkt->_in_use = 0;
            pkt->_packet_size = 0;

        }
    }
    return 0;
}

static void my_callback(u_char *dt,
        const struct pcap_pkthdr *h,
        const u_char *bytes)
{
    static unsigned int packtno;
    my_data *pvt = reinterpret_cast <my_data *> (dt);
    PacketQuer *quer =  pvt->quer;
    Packet pkt;

    pkt._packet_size = h->caplen;
    memcpy(pkt._data, bytes, h->caplen);
    quer->addToQue(pkt);

#ifdef DEBUG_ME
    cout << "Packet captured by "  
        << pvt->dev_ptr << " " << ++packtno << "  \n";
#endif
   
}

void *filter_routine(void *arg)
{
    my_data *dt = (my_data *) arg;
    pcap_t *hnd = dt->handle;
    

    cout << "Thread for " << dt->dev_ptr  << endl;

    if (pcap_loop(hnd , -1, my_callback, (u_char *)dt) < 0) {
        cout << "There was a problem \n";
        pcap_perror(hnd, "OJJJJJ ");
        //pcap_close(hnd);
        throw "Pcap loop problem\n";
    }

    delete dt;

    return NULL;
}

PacketFilter::PacketFilter(PacketQuer *quer, 
        vector <string>& devices, string filter_string) 
                                        throw (const char *)
{
    bpf_u_int32 net = 0, mask = 0;
    char *dev;
    pcap_t *hnd;
    unsigned int i;
    vector <pthread_t>  threads;
    pthread_t t1;
    my_data  *pvt;

    const char *filter = filter_string.c_str();
    char err_buf[PCAP_ERRBUF_SIZE];
    int snaplen = 1600;

    struct bpf_program bp;
    _quer = quer;

    for (i = 0; i < devices.size(); i++) { 

        if (devices[i].size() == 0) continue;

        dev = (char *) devices[i].c_str();
        
        cout << "Filtering for " << dev << endl;

        if ((hnd = pcap_open_live(dev, snaplen, 1, 
                           0, err_buf)) == NULL ) {
            throw "Could not open device";
        }

        unsigned int  addr_p = reinterpret_cast <unsigned int> (hnd);
        cout << "Addr is " << addr_p << endl;

        if (pcap_lookupnet(dev, &net, &mask, err_buf ) < 0) {
            pcap_close(hnd);
            hnd = 0;
            throw "Could not get netmask info\n";
        }

        if (filter_string.size() > 0) {
            if (pcap_compile(hnd, &bp, (char *)filter, 1, mask) < 0 )
                throw "Unable to set filter\n"; 

            if (pcap_setfilter(hnd, &bp) < 0)
                throw "Unable to set filter \n";
        }

        _handles.push_back(hnd);

        // create the threads packet filtering 
        
        pvt = new my_data;
        pvt->handle = hnd;
        pvt->quer = _quer;

        strcpy(pvt->dev_ptr, dev);
        
        pthread_create(&t1, NULL, filter_routine, pvt); 
        threads.push_back(t1);
    }
/*
    for (i = 0; i < threads.size(); i++ ) {
        pthread_join(threads[i], NULL);
    }*/
}


PacketFilter::PacketFilter(PacketQuer *quer, 
        string filter_string) throw (const char *)
{
    bpf_u_int32 net = 0, mask = 0;
    char *dev = "lo";
    pcap_t *hnd;
    _quer = quer;
    char err_buf[PCAP_ERRBUF_SIZE];
    int snaplen = 1600;

    struct bpf_program bp;
    
    if ((hnd = pcap_open_live(dev, snaplen, 1, 
                       0, err_buf)) == NULL ) {
        throw "Could not open device";
    }
    if (pcap_lookupnet(dev, &net, &mask, err_buf ) < 0) {
        pcap_close(hnd);
        throw "Could not get netmask info\n";
    }

    if (filter_string.size() > 0 ){

        if (pcap_compile(hnd, &bp, (char *)(filter_string.c_str()), 
                    1, mask) < 0 )
            throw "Unable to set filter1\n"; 
        if (pcap_setfilter(hnd, &bp) < 0)
            throw "Unable to set filter2 \n";

    }

    _handles.push_back(hnd);

    if (pcap_loop(hnd , -1, my_callback, NULL) < 0) {
        pcap_close(hnd);
        throw "Pcap loop problem\n";
    }
}

PacketFilter::~PacketFilter()
{
    unsigned int i;
    for (i = 0; i < _handles.size(); i++)
        pcap_close(_handles[i]);    
}


